package com.bstek.bdf4.utils.file;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.bstek.bdf4.exception.UtilException;
import lombok.AllArgsConstructor;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *  文件工具类
 */
@UtilityClass
@Slf4j
public class BaseFileUtils {


    /**
     * 拆分文件
     * @param fileFullName 待拆分的完整文件名
     * @param byteSize 按多少字节大小拆分
     * @param solid true 表示最后分片不小于byteSize,若最后分片小于byteSize则与前一个合并，false表示最后一个分片可以小于byteSize
     * @return 拆分后的文件名列表
     */
    public List<String> splitBySize(String fileFullName, long byteSize, boolean solid) {
        if (StrUtil.isEmpty(fileFullName)) {
            throw new IllegalArgumentException("fileFullName is null");
        }
        if (byteSize <= 0) {
            throw new IllegalArgumentException("byteSize must more than 0");
        }
        List<String> parts = new ArrayList<String>();
        File file = new File(fileFullName);
        int count = 0;
        count = (int) Math.ceil(file.length() / (double) byteSize);
        long mod = file.length() % byteSize;
        if (mod > 0) {
            if (solid) {
                count--;
            }
        }
        int countLen = (count + "").length();

        final ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 8, 1000,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < count; i++) {
            String partFileName = FileUtil.getAbsolutePath(file) + StrUtil.DOT + StrUtil.fillBefore((i+1)+"",'0',countLen);
            long readSize = byteSize;
            if (solid && i == count -1) {
                readSize = byteSize+mod;
            }
            executor.execute(new SplitRunnable(readSize,i*byteSize,partFileName,file));
            parts.add(partFileName);
        }

        executor.shutdown();

        while(true){
            if(executor.isTerminated()){
                log.debug("所有的子线程都结束了！");
                break;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                log.error("等待线程池结束时，发生中断异常",e);
            }
        }
        return parts;
    }

    /**
     * 合并文件
     * @param partFiles 拆分文件数组，必须有序
     * @param partFileSize 拆分文件的字节数大小
     * @param mergeFileName 合并后的文件名
     */
    public void mergePartFiles(File[] partFiles, long partFileSize, String mergeFileName) {
        if (partFiles == null || partFiles.length<=0) {
            throw new IllegalArgumentException("partFiles is null");
        }
        if (partFileSize <= 0) {
            throw new IllegalArgumentException("partFileSize must more than 0");
        }
        if (StrUtil.isEmpty(mergeFileName)) {
            throw new IllegalArgumentException("mergeFileName is null");
        }
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(mergeFileName, "rw");
            randomAccessFile.setLength(partFileSize * (partFiles.length - 1)
                    + partFiles[partFiles.length - 1].length());
            randomAccessFile.close();
            final ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 8, 1000,
                    TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());

            for (int i = 0; i < partFiles.length; i++) {
                executor.execute(new MergeRunnable(i * partFileSize,mergeFileName,partFiles[i]));
            }

            executor.shutdown();

            while(true){
                if(executor.isTerminated()){
                    log.debug("所有的子线程都结束了！");
                    break;
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    log.error("等待线程池结束时，发生中断异常",e);
                }
            }

        } catch (IOException e) {
            log.error("merge file error",e);
            throw new UtilException("merge file error",e);
        }
    }

    public String setContentType(String returnFileName){
        String contentType = "application/octet-stream";
        if (returnFileName.lastIndexOf(".") < 0)
            return contentType;
        returnFileName = returnFileName.toLowerCase();
        returnFileName = returnFileName.substring(returnFileName.lastIndexOf(".")+1);
        if (returnFileName.equals("html") || returnFileName.equals("htm") || returnFileName.equals("shtml")){
            contentType = "text/html";
        } else if (returnFileName.equals("css")){
            contentType = "text/css";
        } else if (returnFileName.equals("xml")){
            contentType = "text/xml";
        } else if (returnFileName.equals("gif")){
            contentType = "image/gif";
        } else if (returnFileName.equals("jpeg") || returnFileName.equals("jpg")){
            contentType = "image/jpeg";
        } else if (returnFileName.equals("js")){
            contentType = "application/x-javascript";
        } else if (returnFileName.equals("atom")){
            contentType = "application/atom+xml";
        } else if (returnFileName.equals("rss")){
            contentType = "application/rss+xml";
        } else if (returnFileName.equals("mml")){
            contentType = "text/mathml";
        } else if (returnFileName.equals("txt")){
            contentType = "text/plain";
        } else if (returnFileName.equals("jad")){
            contentType = "text/vnd.sun.j2me.app-descriptor";
        } else if (returnFileName.equals("wml")){
            contentType = "text/vnd.wap.wml";
        } else if (returnFileName.equals("htc")){
            contentType = "text/x-component";
        } else if (returnFileName.equals("png")){
            contentType = "image/png";
        } else if (returnFileName.equals("tif") || returnFileName.equals("tiff")){
            contentType = "image/tiff";
        } else if (returnFileName.equals("wbmp")){
            contentType = "image/vnd.wap.wbmp";
        } else if (returnFileName.equals("ico")){
            contentType = "image/x-icon";
        } else if (returnFileName.equals("jng")){
            contentType = "image/x-jng";
        } else if (returnFileName.equals("bmp")){
            contentType = "image/x-ms-bmp";
        } else if (returnFileName.equals("svg")){
            contentType = "image/svg+xml";
        } else if (returnFileName.equals("jar") || returnFileName.equals("var") || returnFileName.equals("ear")){
            contentType = "application/java-archive";
        } else if (returnFileName.equals("doc")){
            contentType = "application/msword";
        } else if (returnFileName.equals("pdf")){
            contentType = "application/pdf";
        } else if (returnFileName.equals("rtf")){
            contentType = "application/rtf";
        } else if (returnFileName.equals("xls")){
            contentType = "application/vnd.ms-excel";
        } else if (returnFileName.equals("ppt")){
            contentType = "application/vnd.ms-powerpoint";
        } else if (returnFileName.equals("7z")){
            contentType = "application/x-7z-compressed";
        } else if (returnFileName.equals("rar")){
            contentType = "application/x-rar-compressed";
        } else if (returnFileName.equals("swf")){
            contentType = "application/x-shockwave-flash";
        } else if (returnFileName.equals("rpm")){
            contentType = "application/x-redhat-package-manager";
        } else if (returnFileName.equals("der") || returnFileName.equals("pem") || returnFileName.equals("crt")){
            contentType = "application/x-x509-ca-cert";
        } else if (returnFileName.equals("xhtml")){
            contentType = "application/xhtml+xml";
        } else if (returnFileName.equals("zip")){
            contentType = "application/zip";
        } else if (returnFileName.equals("mid") || returnFileName.equals("midi") || returnFileName.equals("kar")){
            contentType = "audio/midi";
        } else if (returnFileName.equals("mp3")){
            contentType = "audio/mpeg";
        } else if (returnFileName.equals("ogg")){
            contentType = "audio/ogg";
        } else if (returnFileName.equals("m4a")){
            contentType = "audio/x-m4a";
        } else if (returnFileName.equals("ra")){
            contentType = "audio/x-realaudio";
        } else if (returnFileName.equals("3gpp") || returnFileName.equals("3gp")){
            contentType = "video/3gpp";
        } else if (returnFileName.equals("mp4") ){
            contentType = "video/mp4";
        } else if (returnFileName.equals("mpeg") || returnFileName.equals("mpg") ){
            contentType = "video/mpeg";
        } else if (returnFileName.equals("mov")){
            contentType = "video/quicktime";
        } else if (returnFileName.equals("flv")){
            contentType = "video/x-flv";
        } else if (returnFileName.equals("m4v")){
            contentType = "video/x-m4v";
        } else if (returnFileName.equals("mng")){
            contentType = "video/x-mng";
        } else if (returnFileName.equals("asx") || returnFileName.equals("asf")){
            contentType = "video/x-ms-asf";
        } else if (returnFileName.equals("wmv")){
            contentType = "video/x-ms-wmv";
        } else if (returnFileName.equals("avi")){
            contentType = "video/x-msvideo";
        }
        return contentType;
    }

    public String toUtf8String(String s) {
        StringBuffer sb = new StringBuffer();
        int len = s.toCharArray().length;
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (c >= 0 && c <= 255) {
                sb.append(c);
            } else {
                byte[] b;
                try {
                    b = Character.toString(c).getBytes("utf-8");
                } catch (Exception ex) {
                    log.error("get bytes for utf-8 error",ex);
                    b = new byte[0];
                }
                for (int j = 0; j < b.length; j++) {
                    int k = b[j];
                    if (k < 0)
                        k += 256;
                    sb.append("%" + Integer.toHexString(k).toUpperCase());
                }
            }
        }
        String s_utf8 = sb.toString();
        sb.delete(0, sb.length());
        sb.setLength(0);
        sb = null;
        return s_utf8;
    }



}
@AllArgsConstructor
@Slf4j
class SplitRunnable implements Runnable {
    private final  Long byteSize;
    private final  long startPos;
    private final  String partFileName;
    private final  File originFile;
    @Override
    public void run() {
        RandomAccessFile rFile = null;
        OutputStream os = null;

        try {
            rFile = new RandomAccessFile(originFile, "r");
            byte[] b = new byte[byteSize.intValue()];
            // 移动指针到每“段”开头
            rFile.seek(startPos);
            int s = rFile.read(b);
            os = new FileOutputStream(partFileName);
            os.write(b, 0, s);
            os.flush();
        } catch (IOException e) {
            log.error("split file error",e);
            throw new UtilException("split file error",e);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error("close file error",e);
                    throw new UtilException("close file error",e);
                }
            }
            if (rFile != null) {
                try {
                    rFile.close();
                } catch (IOException e) {
                    throw new UtilException("close file error",e);
                }
            }

        }

    }
}

@AllArgsConstructor
@Slf4j
class MergeRunnable implements Runnable {
    private final long startPos;
    private final String mergeFileName;
    private final File partFile;

    @Override
    public void run() {
        RandomAccessFile rFile = null;
        FileInputStream fs = null;
        try {
            rFile = new RandomAccessFile(mergeFileName, "rw");
            rFile.seek(startPos);
            fs = new FileInputStream(partFile);
            byte[] b = new byte[fs.available()];
            fs.read(b);
            rFile.write(b);
        } catch (IOException e) {
            log.error("merge file error",e);
            throw new UtilException("merge file error",e);
        } finally {
            if (rFile != null) {
                try {
                    rFile.close();
                } catch (IOException e) {
                    log.error("close rfile error",e);
                    throw new UtilException("close rfile error",e);
                }
            }
            if (fs != null) {
                try {
                    fs.close();
                } catch (IOException e) {
                    log.error("close partfile error",e);
                    throw new UtilException("close partfile error",e);
                }
            }
        }

    }
}

